package com.dongzili.starrocks.common.config.mybatisplus;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

@Configuration
@Slf4j
@AutoConfigureAfter(SpringUtil.class)
public class MybatisPlusMapperConfig {

    public static final Map<String, Class<?>> classMap = new HashMap<>();
    @Resource
    private SqlSessionFactory sqlSessionFactory;

    private static final List<String> BASE_PACKAGES = Arrays.asList(
            "com.dongzili.starrocks.custom.entity"
    );

    // 根据类名获取 bean name
    private String getBeanName(String className) {
        int index = className.lastIndexOf(".");
        String simpleClassName = index != -1 ? className.substring(index + 1) : className;
        char firstChar = simpleClassName.charAt(0);
        if (firstChar >= 'A' && firstChar <= 'Z') {
            firstChar -= (char) ('A' - 'a');
        }
        return firstChar + simpleClassName.substring(1);
    }

    @PostConstruct
    public void setup() {
        if (sqlSessionFactory == null) {
            log.error("SqlSessionFactory 未正确注入，请检查配置。");
            return;
        }
        for (String packageName : BASE_PACKAGES) {
            if (!StringUtils.hasText(packageName)) {
                continue;
            }
            // 扫描数据库实体，根据实体创建对应 mapper、service
            Set<Class<?>> entityClassSet = ClassUtil.scanPackage(packageName);
            if (entityClassSet.isEmpty()) {
                continue;
            }
            for (Class<?> entityClass : entityClassSet) {
                // 只创建带有 MapAndService 注解的实体
                TableName tableName = entityClass.getAnnotation(TableName.class);
                if (tableName == null) {
                    continue;
                }

                classMap.put(tableName.value(), entityClass);
                String mapperClassName = packageName + ".mapper" + "." + entityClass.getSimpleName() + "AutoMapper";
                String serviceImplClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoServiceImpl";
                String serviceClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoService";

                Class<?> mapperClass = null;
                Class<?> serviceImplClass = null;
                Class<?> serviceClass = null;

                /* 创建 mapper */
                try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
                    try (DynamicType.Unloaded<?> mapperType = new ByteBuddy()
                            .makeInterface(TypeDescription.Generic.Builder.parameterizedType(BaseMapper.class, entityClass).build())
                            .name(mapperClassName)
                            .annotateType(AnnotationDescription.Builder.ofType(Mapper.class).build())
                            .make()) {
                        mapperClass = mapperType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                                .getLoaded();
                    }
                    MapperFactoryBean<?> factoryBean = new MapperFactoryBean<>(mapperClass);
                    factoryBean.setSqlSessionFactory(sqlSessionFactory);
//                    sqlSession.getConfiguration().setDefaultStatementTimeout(120*1000);
                    sqlSession.getConfiguration().addMapper(mapperClass);
                    SpringUtil.registerBean(getBeanName(mapperClassName), factoryBean.getObject());
                    log.info("自动注册 mybatisplus mapper bean -> 名称:{}, 类名：{}", getBeanName(mapperClassName), mapperClassName);
                } catch (Exception e) {
                    log.error("自动注册 mybatisplus mapper bean 失败，原因：{}", e.getMessage(), e);
                    throw new RuntimeException("自动注册 mybatisplus mapper bean 失败!"+mapperClassName, e);
                }

                /* 创建 service */
                try {
                    try (DynamicType.Unloaded<?> serviceType = new ByteBuddy()
                            .makeInterface(TypeDescription.Generic.Builder.parameterizedType(IService.class, entityClass).build())
                            .name(serviceClassName)
                            .make()) {
                        serviceClass = serviceType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                                .getLoaded();
                    }

                    List<AnnotationDescription> annotations = new ArrayList<>();
                    annotations.add(AnnotationDescription.Builder.ofType(Service.class).build());
                    annotations.add(AnnotationDescription.Builder.ofType(Transactional.class).build());
//                    if (mapperClass == null) {
//                        throw new RuntimeException("未找到 mapper 类：" + mapperClassName);
//                    }
                    try (DynamicType.Unloaded<?> serviceImplType = new ByteBuddy()
                            .subclass(TypeDescription.Generic.Builder.parameterizedType(ServiceImpl.class, mapperClass, entityClass).build())
                            .implement(serviceClass)
                            .name(serviceImplClassName)
                            .annotateType(annotations)
                            .make()) {
                        serviceImplClass = serviceImplType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                                .getLoaded();
                    }
                    SpringUtil.registerBean(getBeanName(serviceImplClassName), serviceImplClass.newInstance());
                    log.info("自动注册 mybatisplus Service Bean -> 名称:{}, 类名：{}", getBeanName(serviceImplClassName), serviceImplClassName);
                } catch (Exception e) {
                    log.error("自动注册 mybatisplus Service Bean 失败，原因：{}", e.getMessage(), e);
                    throw new RuntimeException("自动注册 mybatisplus mapper bean 失败!"+serviceImplClassName, e);
                }
            }
        }
    }
}